//
// Created by JYL on 2025/1/23.
//
/**
 ****************************************************************************************************
 * @file        stmflash.c
 * @author      935848559@qq.com
 * @version     V1.0
 * @date        2024-03-12
 * @brief       STM32G0内部FLASH读写 驱动代码
 * @license     Copyright (c)
 ****************************************************************************************************
 * @attention
 *
 *
 ****************************************************************************************************
 */
#include "stmflash.h"
#include "stdio.h"

uint32_t addr = OTA_ADDR;   //最新的Flash地址



/**
 * @brief       从指定地址读取一个字节 (8位数据)stm32是小端模式
 * @param       faddr   : 读取地址
 * @retval      读取到的数据 (8位)
 */
uint8_t stmflash_read_byte(uint32_t faddr)
{
    /* 读取的话就简单了，直接使用地址即可 */
    return *(volatile uint8_t *)faddr;
}

/**
 * @brief       flash 测试
 * @param       address    开头地址
 * @param       *buf       写入数据
 * @param       length     写入字节数据长度
 * @retval      void
 */
void Flash_test()   //uint8_t *buf, uint32_t length
{
//    Write_Flash(OTA_ADDR,buf,length);       //写大数据进入

//    if(Write_Config(UPDATE_FLAG) != FLASH_OK)   //写标志位数据
//        printf("Write_APP ERROR %x\n",addr);

    printf("UPDATE_FLAG\r\n");
    uint64_t data = UPDATE_FLAG;
    Write_Flash(CONFIG_ADDR,(uint8_t *)&data,4);//正确，取uint64_t数据的地址，然后强制转成(uint8_t *)
    HAL_Delay(5000);
    printf("read config\n");
    printf("CONFIG_ADDR  = %x\n",stmflash_read_byte(CONFIG_ADDR));
    printf("CONFIG_ADDR+1  = %x\n",stmflash_read_byte(CONFIG_ADDR+1));
    printf("CONFIG_ADDR+2  = %x\n",stmflash_read_byte(CONFIG_ADDR+2));
    printf("CONFIG_ADDR+3  = %x\n",stmflash_read_byte(CONFIG_ADDR+3));
    printf("CONFIG_ADDR+4  = %x\n",stmflash_read_byte(CONFIG_ADDR+4));
    printf("CONFIG_ADDR+5  = %x\n",stmflash_read_byte(CONFIG_ADDR+5));

}







/**
 * @brief       获取 地址Addr 在 Flash中式第几页。根据每页大小2k
 * @param       Addr    开头地址
 * @retval      FLASH页数
 */
static uint32_t GetPage(uint32_t Addr)
{
    uint32_t page = 0;
    page = (Addr-FLASH_BASE) / FLASH_PAGE_SIZE;
    return page;
}

/**
 * @brief       擦除指定位置和大小的Flash,擦除flash之前必须解锁flash。
 * @param       addr    开头地址
 * @param       num    内存大小/kb
 * @retval      FLASH_SATAE_T state
 */
static FLASH_SATAE_T Erase_Flash(uint32_t addr,uint8_t num)
{
    uint32_t SectorError=0;
    FLASH_EraseInitTypeDef UPDATE_FLASH;

    UPDATE_FLASH.TypeErase = FLASH_TYPEERASE_PAGES;
    UPDATE_FLASH.Page = GetPage(addr);
    UPDATE_FLASH.NbPages = num;
    if(HAL_FLASHEx_Erase(&UPDATE_FLASH,&SectorError) != HAL_OK) {
        printf("ERASE ERROR %x\n",SectorError);
        goto ERROR;
    }
    printf("ERASE SUCCESS %x\n",SectorError);
    return FLASH_OK;
    ERROR:
    return FLASH_ERROR;
}

/**
 * @brief       写config Flash，升级标志位
 * @param       data     64位双字节
 * @retval      FLASH_SATAE_T state
 */
FLASH_SATAE_T Write_Config(uint64_t data)
{
    HAL_FLASH_Unlock();//解锁flash
    if(Erase_Flash(CONFIG_ADDR,1) != FLASH_OK) { //擦除flash
        goto ERROR;
    }
    if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,CONFIG_ADDR,data) != HAL_OK) {
        printf("Write_Config ERROR\n");
        goto ERROR;
    }
    HAL_FLASH_Lock();//上锁
    printf("Write_Config SUCCESS\n");
    return FLASH_OK;
    ERROR:
    HAL_FLASH_Lock();//上锁
    return FLASH_ERROR;
}

/**
 * @brief       写Flash,这里包括擦除flash，每页Flash是2Kb
 * @param       address    开头地址
 * @param       *buf       写入数据
 * @param       length     写入字节数据长度
 * @retval      FLASH_SATAE_T state
 */
FLASH_SATAE_T Write_Flash(uint32_t start_address, uint8_t *buf, uint32_t length)
{
    uint32_t i,j,len =0;
    uint64_t data = 0;
    uint8_t num = 1;

    if(length == 0)     //长度为0时直接返回
    {
        goto ERROR;
    }
    if(length % 8)       //长度非8字节倍数时则补齐新的8字节
        len = length/8+1;
    else
        len = length/8;

    while(length > num * 2048) num++;   //得出需要擦除几页Flash

    HAL_FLASH_Unlock();//解锁flash
    if(Erase_Flash(start_address , num) != FLASH_OK)   //擦除Flash
    {
        printf("Erase_Flash ERROR\n");
        goto ERROR;
    }

    for(i=0;i<len;i++)//这个是要转多少组
    {
        data = 0;
        for(j=0;j<8;j++)//这个是转8个字节，64位数据
        {
            data|=(uint64_t)buf[j+i*8]<<(j*8);
        }
        if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,start_address,data) != HAL_OK)
        {
            goto ERROR;
        }
        start_address += 8;
    }
    HAL_FLASH_Lock();//上锁
    addr = start_address;   //记录最新的地址
    printf("Write_Flash_Data SUCCESS\n");
    return FLASH_OK;
    ERROR:
    HAL_FLASH_Lock();//上锁
    printf("Write_Flash_Data ERROR\n");
    return FLASH_ERROR;
}


